/** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * */ package org.apache.airavata.gfac.impl.task.utils.bes; import eu.emi.security.authn.x509.CommonX509TrustManager; import eu.emi.security.authn.x509.X509CertChainValidator; import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.crypto.params.AsymmetricKeyParameter; import org.bouncycastle.crypto.util.PrivateKeyFactory; import org.bouncycastle.crypto.util.PublicKeyFactory; import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.operator.ContentSigner; import org.bouncycastle.operator.bc.BcRSAContentSignerBuilder; import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder; import org.bouncycastle.util.encoders.Base64; import javax.net.ssl.*; import javax.security.auth.login.FailedLoginException; import java.io.*; import java.net.ProtocolException; import java.security.*; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; /** * The MyProxyLogon class provides an interface for retrieving credentials from * a MyProxy server. * <p/> * First, use <code>setHost</code>, <code>setPort</code>, * <code>setUsername</code>, <code>setPassphrase</code>, * <code>setCredentialName</code>, <code>setLifetime</code> and * <code>requestTrustRoots</code> to configure. Then call <code>connect</code>, * <code>logon</code>, <code>getCredentials</code>, then * <code>disconnect</code>. Use <code>getCertificates</code> and * <code>getPrivateKey</code> to access the retrieved credentials, or * <code>writeProxyFile</code> or <code>saveCredentialsToFile</code> to * write them to a file. Use <code>writeTrustRoots</code>, * <code>getTrustedCAs</code>, <code>getCRLs</code>, * <code>getTrustRootData</code>, and <code>getTrustRootFilenames</code> * for trust root information. * * (modified for use with UNICORE) * * @version 1.1 * @see <a href="http://myproxy.ncsa.uiuc.edu/">MyProxy Project Home Page</a> * */ public class MyProxyLogon { public final static String version = "1.1"; private enum State { READY, CONNECTED, LOGGEDON, DONE } public final static String VERSION = "VERSION=MYPROXYv2"; private final static String GETCOMMAND = "COMMAND=0"; private final static String TRUSTROOTS = "TRUSTED_CERTS="; private final static String USERNAME = "USERNAME="; private final static String PASSPHRASE = "PASSPHRASE="; private final static String LIFETIME = "LIFETIME="; private final static String CREDNAME = "CRED_NAME="; public final static String RESPONSE = "RESPONSE="; private final static String ERROR = "ERROR="; private final static String DN = "CN=ignore"; public final int DEFAULT_KEY_SIZE = 2048; private int keySize = DEFAULT_KEY_SIZE; private final static String keyAlg = "RSA"; private State state = State.READY; private String host = "localhost"; private String username; private String credname; private char[] passphrase; private int port = 7512; private int lifetime = 43200; private SSLSocket socket; private BufferedInputStream socketIn; private BufferedOutputStream socketOut; private KeyPair keypair; private Collection<X509Certificate> certificateChain; private String[] trustrootFilenames; private String[] trustrootData; private KeyManagerFactory keyManagerFactory; private TrustManager trustManager; /** * Constructs a MyProxyLogon object. */ public MyProxyLogon() { super(); host = System.getenv("MYPROXY_SERVER"); if (host == null) { host = "myproxy.teragrid.org"; } String portString = System.getenv("MYPROXY_SERVER_PORT"); if (portString != null) { port = Integer.parseInt(portString); } username = System.getProperty("user.name"); } /** * sets the internal trust manager using the supplied validator */ public void setValidator(X509CertChainValidator validator){ CommonX509TrustManager mtm = new CommonX509TrustManager(validator); setTrustManager(mtm); } /** * Sets the hostname of the MyProxy server. Defaults to localhost. * * @param host MyProxy server hostname */ public void setHost(String host) { this.host = host; } /** * Sets the port of the MyProxy server. Defaults to 7512. * * @param port MyProxy server port */ public void setPort(int port) { this.port = port; } /** * Sets the key size. * * @param keySize */ public void setKeySize(int keySize) { this.keySize = keySize; } /** * Gets the MyProxy username. * * @return MyProxy server port */ public String getUsername() { return username; } /** * Sets the MyProxy username. Defaults to user.name. * * @param username MyProxy username */ public void setUsername(String username) { this.username = username; } /** * Sets the optional MyProxy credential name. * * @param credname credential name */ public void setCredentialName(String credname) { this.credname = credname; } /** * Sets the MyProxy passphrase. * * @param passphrase MyProxy passphrase */ public void setPassphrase(char[] passphrase) { this.passphrase = passphrase; } /** * Sets the requested credential lifetime. Defaults to 43200 seconds (12 * hours). * * @param seconds Credential lifetime */ public void setLifetime(int seconds) { lifetime = seconds; } /** * Gets the certificates returned from the MyProxy server by * getCredentials(). * * @return Collection of java.security.cert.Certificate objects */ public Collection<X509Certificate> getCertificates() { return certificateChain; } // for unit testing static PrivateKey testingPrivateKey; /** * Gets the private key generated by getCredentials(). * * @return PrivateKey */ public PrivateKey getPrivateKey() { if(testingPrivateKey!=null){ //for unit testing return testingPrivateKey; } return keypair.getPrivate(); } /** * Connects to the MyProxy server at the desired host and port. Requires * host authentication via SSL. The host's certificate subject must * match the requested hostname. If CA certificates are found in the * standard GSI locations, they will be used to verify the server's * certificate. If trust roots are requested and no CA certificates are * found, the server's certificate will still be accepted. */ public void connect() throws IOException, GeneralSecurityException { SSLContext sc = SSLContext.getInstance("SSL"); if(trustManager==null){ throw new IllegalStateException("No trust manager has been set!"); } TrustManager[] trustAllCerts = new TrustManager[]{trustManager}; sc.init(getKeyManagers(), trustAllCerts, new java.security.SecureRandom()); SSLSocketFactory sf = sc.getSocketFactory(); socket = (SSLSocket) sf.createSocket(host, port); socket.startHandshake(); socketIn = new BufferedInputStream(socket.getInputStream()); socketOut = new BufferedOutputStream(socket.getOutputStream()); state = State.CONNECTED; } /** * Set the key manager factory for use in client-side SSLSocket * certificate-based authentication to the MyProxy server. * Call this before connect(). * * @param keyManagerFactory Key manager factory to use */ public void setKeyManagerFactory(KeyManagerFactory keyManagerFactory) { this.keyManagerFactory = keyManagerFactory; } public void setTrustManager(TrustManager trustManager) { this.trustManager = trustManager; } /** * Disconnects from the MyProxy server. */ public void disconnect() throws IOException { socket.close(); socket = null; socketIn = null; socketOut = null; state = State.READY; } /** * Logs on to the MyProxy server by issuing the MyProxy GET command. */ public void logon() throws IOException, GeneralSecurityException { String line; char response; if (state != State.CONNECTED) { connect(); } socketOut.write('0'); socketOut.flush(); socketOut.write(VERSION.getBytes()); socketOut.write('\n'); socketOut.write(GETCOMMAND.getBytes()); socketOut.write('\n'); socketOut.write(USERNAME.getBytes()); socketOut.write(username.getBytes()); socketOut.write('\n'); socketOut.write(PASSPHRASE.getBytes()); socketOut.write(new String(passphrase).getBytes()); socketOut.write('\n'); socketOut.write(LIFETIME.getBytes()); socketOut.write(Integer.toString(lifetime).getBytes()); socketOut.write('\n'); if (credname != null) { socketOut.write(CREDNAME.getBytes()); socketOut.write(credname.getBytes()); socketOut.write('\n'); } socketOut.flush(); line = readLine(socketIn); if (line == null) { throw new EOFException(); } if (!line.equals(VERSION)) { throw new ProtocolException("bad MyProxy protocol VERSION string: " + line); } line = readLine(socketIn); if (line == null) { throw new EOFException(); } if (!line.startsWith(RESPONSE) || line.length() != RESPONSE.length() + 1) { throw new ProtocolException( "bad MyProxy protocol RESPONSE string: " + line); } response = line.charAt(RESPONSE.length()); if (response == '1') { StringBuffer errString; errString = new StringBuffer("MyProxy logon failed"); while ((line = readLine(socketIn)) != null) { if (line.startsWith(ERROR)) { errString.append('\n'); errString.append(line.substring(ERROR.length())); } } throw new FailedLoginException(errString.toString()); } else if (response == '2') { throw new ProtocolException( "MyProxy authorization RESPONSE not implemented"); } else if (response != '0') { throw new ProtocolException( "unknown MyProxy protocol RESPONSE string: " + line); } while ((line = readLine(socketIn)) != null) { if (line.startsWith(TRUSTROOTS)) { String filenameList = line.substring(TRUSTROOTS.length()); trustrootFilenames = filenameList.split(","); trustrootData = new String[trustrootFilenames.length]; for (int i = 0; i < trustrootFilenames.length; i++) { String lineStart = "FILEDATA_" + trustrootFilenames[i] + "="; line = readLine(socketIn); if (line == null) { throw new EOFException(); } if (!line.startsWith(lineStart)) { throw new ProtocolException( "bad MyProxy protocol RESPONSE: expecting " + lineStart + " but received " + line); } trustrootData[i] = new String(Base64.decode(line .substring(lineStart.length()))); } } } state = State.LOGGEDON; } /** * Retrieves credentials from the MyProxy server. */ public void getCredentials() throws IOException, GeneralSecurityException { KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance(keyAlg); keyGenerator.initialize(keySize); keypair = keyGenerator.genKeyPair(); Security.addProvider(new BouncyCastleProvider()); org.bouncycastle.pkcs.PKCS10CertificationRequest pkcs10 = null; try{ pkcs10 = generateCertificationRequest(DN, keypair); } catch(Exception ex){ throw new GeneralSecurityException(ex); } getCredentials(pkcs10.getEncoded()); } public X509Certificate getCertificate() { if (certificateChain == null) { return null; } Iterator<X509Certificate> iter = this.certificateChain.iterator(); return iter.next(); } private KeyManager[] getKeyManagers() { return keyManagerFactory != null? keyManagerFactory.getKeyManagers() : null ; } private void getCredentials(byte[] derEncodedCertRequest) throws IOException, GeneralSecurityException { if (state != State.LOGGEDON) { logon(); } socketOut.write(derEncodedCertRequest); socketOut.flush(); int numCertificates = socketIn.read(); if (numCertificates == -1) { throw new IOException("Error: connection aborted"); } else if (numCertificates == 0 || numCertificates < 0) { throw new GeneralSecurityException("Error: bad number of certificates sent by server"); } CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); certificateChain = new ArrayList<X509Certificate>(); for(int i = 0; i<numCertificates; i++){ X509Certificate c = (X509Certificate)certFactory.generateCertificate(socketIn); certificateChain.add(c); } state = State.DONE; } private String readLine(InputStream is) throws IOException { StringBuffer sb = new StringBuffer(); for (int c = is.read(); c > 0 && c != '\n'; c = is.read()) { sb.append((char) c); } if (sb.length() > 0) { return new String(sb); } return null; } private org.bouncycastle.pkcs.PKCS10CertificationRequest generateCertificationRequest(String dn, KeyPair kp) throws Exception{ X500Name subject=new X500Name(dn); PublicKey pubKey=kp.getPublic(); PrivateKey privKey=kp.getPrivate(); AsymmetricKeyParameter pubkeyParam = PublicKeyFactory.createKey(pubKey.getEncoded()); SubjectPublicKeyInfo publicKeyInfo=SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(pubkeyParam); PKCS10CertificationRequestBuilder builder=new PKCS10CertificationRequestBuilder(subject, publicKeyInfo); AlgorithmIdentifier signatureAi = new AlgorithmIdentifier(OIWObjectIdentifiers.sha1WithRSA); BcRSAContentSignerBuilder signerBuilder=new BcRSAContentSignerBuilder( signatureAi, AlgorithmIdentifier.getInstance(OIWObjectIdentifiers.idSHA1)); AsymmetricKeyParameter pkParam = PrivateKeyFactory.createKey(privKey.getEncoded()); ContentSigner signer=signerBuilder.build(pkParam); return builder.build(signer); } }